package at.codejedi.sto.parser;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Hashtable;
import java.util.List;

import at.codejedi.sto.model.combat.Battle;
import at.codejedi.sto.model.combat.CombatLog;
import at.codejedi.sto.model.combat.CombatLogFile;
import at.codejedi.sto.model.combat.Entry;
import at.codejedi.sto.model.combat.IBattle;
import at.codejedi.sto.model.combat.IPlayer;
import at.codejedi.sto.model.combat.Player;

public class Parser {

	/**
	 * assigns a list of entities to a CombatLogFile
	 * 
	 * @param entries
	 * @return
	 */
	private CombatLogFile createCombatLogFile(List<Entry> entries) {
		CombatLogFile log = new CombatLogFile();
		log.setEntries(entries);
		return log;
	}

	/**
	 * Extracts the list of players from the Combat Log File
	 * 
	 * @param combatLog
	 *            to parse for players
	 * @return List of players contained in the combat log
	 */
	private CombatLog interpret(CombatLogFile combatLog) {
		Hashtable<String, IPlayer> players = new Hashtable<String, IPlayer>();

		List<Entry> entries = combatLog.getEntries();
		for (Entry entry : entries) {

			String fromType = entry.getFromType();
			String toType = entry.getToType();
			// Only Player start their hash with P!
			String playerHandle = null;
			String playerName = null;
			if (fromType.startsWith("P")) {
				playerHandle = fromType;
				playerName = entry.getPlayerName();
			} else if (toType.startsWith("P")) {
				playerHandle = toType;
			}

			if (playerHandle == null)
				continue;

			IPlayer p;
			if (players.containsKey(playerHandle)) {
				p = players.get(playerHandle);
				p.AddEntry(entry);

			} else {
				p = new Player();
				p.setPlayerHandle(playerHandle);
				players.put(playerHandle, p);
			}

			if (playerName != null && p.getPlayerName() == null)
				p.setPlayerName(playerName);
		}

		List<IPlayer> realPlayers = new ArrayList<IPlayer>();
		for (IPlayer player : players.values()) {
			if (player.getPlayerName() != null) {
				player.preLoad();
				if (player.getBattles().size() > 0)
					realPlayers.add(player);
			}

		}

		CombatLog log = new CombatLog();
		log.setPlayers(realPlayers);
		List<IBattle> battles = Battle.createFromPlayers(realPlayers);
		log.setBattles(battles);
		return log;
	}

	/**
	 * Parses a STO Combat Log File and creates a Combat Log entity
	 * 
	 * @param filePath
	 *            file path where the combat log is located
	 * @return parsed combat Log entity
	 * @throws IOException
	 */
	private CombatLogFile parseFile(File filePath) throws IOException {
		List<String> lines = readSmallTextFile(filePath);
		List<Entry> entries = parseLines(lines);
		return createCombatLogFile(entries);
	}

	/**
	 * Interprets line from STO Combat Log and creates an Entry
	 * 
	 * @param line
	 *            complete line of the log
	 * @return LogEntry as an Object representation
	 */
	public Entry parseLine(String line) {
		Entry createdEntry = new Entry();
		String[] columns = line.split(",");
		String timeStampAndName = columns[0];
		setTimeStampAndPlayerName(createdEntry, timeStampAndName);

		String fromType = columns[1];
		createdEntry.setFromType(fromType);

		String skillName = columns[2];
		createdEntry.setSkillName(skillName);

		String skillReference = columns[3];
		createdEntry.setSkillReference(skillReference);

		String toName = columns[4];
		createdEntry.setToName(toName);

		String toHandle = columns[5];
		createdEntry.setToType(toHandle);

		String damageSourceName = columns[6];
		createdEntry.setDamageSourceName(damageSourceName);

		@SuppressWarnings("unused")
		String unknown = columns[7];
		String damageType = columns[8];
		createdEntry.setDamageType(damageType);

		String modifier = columns[9];
		createdEntry.setModifier(modifier);

		String netDamage = columns[10];
		createdEntry.setNetDamage(Double.parseDouble(netDamage));

		String rawDamage = columns[11];
		createdEntry.setRawDamage(Double.parseDouble(rawDamage));

		return createdEntry;
	}

	/**
	 * Parse lines and create and entity for every line
	 * 
	 * @param lines
	 * @return list of entities
	 */
	private List<Entry> parseLines(List<String> lines) {
		List<Entry> entries = new ArrayList<Entry>();
		for (String line : lines) {
			Entry entry = parseLine(line);
			entries.add(entry);
		}
		return entries;
	}

	/**
	 * Parses a STO Combat Log file and returns the Players
	 * 
	 * @param filePath
	 *            file to parse
	 * @return List of players from the combat file
	 * @throws IOException
	 */
	public CombatLog read(File filePath) throws IOException {
		CombatLogFile log = parseFile(filePath);
		return interpret(log);
	}

	/**
	 * Reads all lines of a given textfile
	 * 
	 * @param file
	 *            file to read from
	 * @return List of string which represents the lines
	 * @throws IOException
	 */
	List<String> readSmallTextFile(File file) throws IOException {
		List<String> lines = new ArrayList<String>();
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;

		while ((strLine = br.readLine()) != null) {
			lines.add(strLine);

		}
		// Close the input stream
		in.close();
		return lines;
	}

	/**
	 * Set the TimeStamp and Player Name property on the given entry
	 * 
	 * @param entry
	 *            to set Timestamp on
	 * @param timeStampAndName
	 *            String which contains the timestamp
	 *            13:01:29:21:29:13.9::Marcus
	 */
	private void setTimeStampAndPlayerName(Entry entry, String timeStampAndName) {
		String[] details = timeStampAndName.split(":");
		int year = Integer.parseInt(details[0]) + 2000;
		int month = Integer.parseInt(details[1]);
		int day = Integer.parseInt(details[2]);
		int hour = Integer.parseInt(details[3]);
		int minutes = Integer.parseInt(details[4]);
		String secondsAndMillis = details[5];
		if (details.length > 6) {
			String playerName = details[7];
			entry.setPlayerName(playerName);
		}
		String[] secondsSplit = secondsAndMillis.split("\\.");
		int seconds = Integer.parseInt(secondsSplit[0]);
		int milliseconds = Integer.parseInt(secondsSplit[1] + "00");
		Calendar c = Calendar.getInstance();
		c.set(year, month, day, hour, minutes, seconds);
		c.add(Calendar.MILLISECOND, milliseconds);
		entry.setTime(c);

	}
}
